### 距离度量
import math
from itertools import combinations
## 定义距离函数
def L(x,y,p=2):
    if len(x)==len(y) and len(x)>1:
        sum=0
        for i in range(0,len(x)):
            sum+=math.pow(abs(x[i]-y[i]),p)##Math.pow(x,y),返回 x 的 y 次幂的值
        return math.pow(sum,1/p)
    else:
        return 0
###课本例3.1
x1=[1,1,2,3];x2=[5,1,4,3];x3=[4,4,6,6]
X=[x1,x2,x3]
for i in range(1,5):
    r1 = {str(j)+'-'+str(j+1): L(X[j],X[j+1],p=i) for j in range(0,len(X)-1)}
    r2 = {str(j)+'-'+str(j+2): L(X[j],X[j+2],p=i) for j in range(0,len(X)-2)}#计算任意两个点之间的距离，分为1步长和2步长
r = dict(r1, **r2)##实现字典合并
print(r)
print(min(zip(r.values(),r.keys())))
#zip函数将对象中对应的元素打包成一个个元组，然后返回由这些元组组成的列表,min函数比较这个元组的第一个值，所以把value放前面
#这样既可以保留value，也可以保留key
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter

# data
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
print(type(df))
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()#显示图例
plt.show()
data = np.array(df.iloc[:100, [0, 1, -1]])##.iloc按行号获取行数据，后面还可以选择特定列
X, y = data[:,:-1], data[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

class KNN:
    def __init__(self, X_train, y_train, n_neighbors=3, p=2):
        """
        parameter: n_neighbors 临近点个数
        parameter: p 距离度量
        """
        self.n = n_neighbors
        self.p = p
        self.X_train = X_train
        self.y_train = y_train

    def predict(self, X):
        # 取出n个点
        knn_list = []
        for i in range(self.n):
            dist = np.linalg.norm(X - self.X_train[i], ord=self.p)
            knn_list.append((dist, self.y_train[i]))

        for i in range(self.n, len(self.X_train)):
            max_index = knn_list.index(max(knn_list, key=lambda x: x[0]))
            dist = np.linalg.norm(X - self.X_train[i], ord=self.p)
            if knn_list[max_index][0] > dist:
                knn_list[max_index] = (dist, self.y_train[i])

        # 统计
        knn = [k[-1] for k in knn_list]
        count_pairs = Counter(knn)
        max_count = sorted(count_pairs, key=lambda x: x)[-1]
        return max_count

    def score(self, X_test, y_test):
        right_count = 0
        n = 10
        for X, y in zip(X_test, y_test):
            label = self.predict(X)
            if label == y:
                right_count += 1
        return right_count / len(X_test)